package com.stu.moudle.config;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.stu.moudle.config.bean.RedisProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


@Configuration
public class RedisConfig {

    @Autowired
    private RedisProperties redisProperties;
    /**
     * Redis连接池的配置
     *
     * @return JedisPoolConfig
     */
    @Bean
    public JedisPoolConfig getJedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲数
        jedisPoolConfig.setMaxIdle(redisProperties.getPool().getMaxIdle());

        // 连接池的最大数据库连接数
        jedisPoolConfig.setMaxTotal(redisProperties.getPool().getMaxTotal());
        // 最大建立连接等待时间
        jedisPoolConfig.setMaxWaitMillis(redisProperties.getPool().getMaxWaitMillis());
        // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(redisProperties.getPool().getMinEvictableIdleTimeMillis());
        // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(redisProperties.getPool().getNumTestsPerEvictionRun());
        // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(redisProperties.getPool().getTimeBetweenEvictionRunsMillis());
        // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
        jedisPoolConfig.setTestOnBorrow(redisProperties.getPool().isTestOnBorrow());
        // 在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(redisProperties.getPool().isTestWhileIdle());
        return jedisPoolConfig;
    }

    @Bean
    @ConditionalOnProperty(prefix = "redis.config",name = "model",havingValue = "cluster")
    public RedisClusterConfiguration redisClusterConfiguration() {
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        Set<RedisNode> nodes = new HashSet<>();
        String ips = redisProperties.getClusterNodes();
        Assert.notNull(ips,"redis cluster model nodes is not null");
        List<String> IP_PORT = Arrays.asList(ips.trim().split(","));
        for (int i = 0; i < IP_PORT.size(); i++) {
            String ipAndPort = IP_PORT.get(i);
            String[] ipAndPorts = ipAndPort.trim().split(":");
            nodes.add(new RedisNode(ipAndPorts[0], Integer.valueOf(ipAndPorts[1])));
        }
        redisClusterConfiguration.setClusterNodes(nodes);
        redisClusterConfiguration.setMaxRedirects(redisProperties.getMaxRedirects());
        redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        return redisClusterConfiguration;
    }

    @Bean
    @ConditionalOnProperty(prefix = "redis.config",name = "model",havingValue = "sentinel")
    public RedisSentinelConfiguration redisSentinelConfiguration() {
        RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
        Set<RedisNode> nodes = new HashSet<>();
        String IP_PORT = redisProperties.getSentinelNodes();
        Assert.notNull(IP_PORT,"redis sentinel model nodes is not null");
        List<String> ip_ports = Arrays.asList(IP_PORT.trim().split(","));
        for (int i = 0; i < ip_ports.size(); i++) {
            String[] ip_port = ip_ports.get(i).split(":");
            nodes.add(new RedisNode(ip_port[0], Integer.valueOf(ip_port[1])));
        }
        redisSentinelConfiguration.setMaster(redisProperties.getMaster());
        redisSentinelConfiguration.setSentinels(nodes);
        redisSentinelConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        return redisSentinelConfiguration;
    }



    /**
     * redis连接工厂类
     *
     * @return JedisConnectionFactory
     */
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = null;

        if (redisProperties.getModel().equals(RedisProperties.CLUSTER)){
            // 集群模式
            jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration(), getJedisPoolConfig());
        }else if(redisProperties.getModel().equals(RedisProperties.SENTINEL)){
            // 哨兵模式
            jedisConnectionFactory = new JedisConnectionFactory(redisSentinelConfiguration(), getJedisPoolConfig());
        }else if (redisProperties.getModel().equals(RedisProperties.SINGLETON)){
            //单机模式
            String hostName = redisProperties.getHostName();
            Integer port = redisProperties.getPort();
            Assert.notNull(hostName,"redis singleton model host must not be null.");
            Assert.notNull(port,"redis singleton model port must not be null.");
            RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            redisStandaloneConfiguration.setHostName(hostName);
            redisStandaloneConfiguration.setPassword(redisProperties.getPassword());
            redisStandaloneConfiguration.setPort(port);
            jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
        }
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }

    /**
     * 实例化 RedisTemplate 对象
     *
     * @return RedisTemplate<String , Object>
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // Template初始化
        initDomainRedisTemplate(redisTemplate);
        return redisTemplate;
    }

    /**
     * 设置数据存入 redis 的序列化方式 使用默认的序列化会导致key乱码
     */
    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        // 开启redis数据库事务的支持
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        // 如果不配置Serializer，那么存储的时候缺省使用String，如果用User类型存储，那么会提示错误User can't cast to
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // fastJson序列化对象设置
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
    }



}
